﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using Axiom.Graphics;
using System.IO;
using Axiom.Configuration;
using Axiom.Graphics.Collections;
using Axiom.Core;

namespace GameDesigner
{
    partial class Descriptor
    {
        private List<string> ignoredSceneNodeNames = new List<string>();

        public void SaveToFile(string FileAddress, ConfigOptionCollection configOptions)
        {
            SaveToFile(FileAddress, configOptions, null, null);
        }

        public void SaveToFile(string FileAddress, ConfigOptionCollection configOptions, SceneNode RootSceneNode, List<string> ignoredSceneNodes)
        {
            if (!IsInitialized)
                throw new Exception("Descriptor needs to be initialized before use.");

            System.Diagnostics.Debug.Write("Writing out XML engine descriptor started . . .");

            if(ignoredSceneNodes != null)
                foreach (string nName in ignoredSceneNodes)
                {
                    if (!ignoredSceneNodeNames.Contains(nName))
                        ignoredSceneNodeNames.Add(nName);
                }

            using (XmlWriter engineDescriptor = XmlWriter.Create(FileAddress))
            {
                engineDescriptor.WriteStartDocument();
                engineDescriptor.WriteStartElement("GameDesignerEngineDescriptor");
                engineDescriptor.WriteAttributeString("version", "0.8.0.0");

                WriteCoreConfigs(engineDescriptor, configOptions);

                WriteTerrainConfig(engineDescriptor);

                WriteResourcesConfig(engineDescriptor, Path.GetDirectoryName(FileAddress));

                if (RootSceneNode != null)
                    WriteScene(engineDescriptor, RootSceneNode);

                engineDescriptor.WriteEndElement();
                engineDescriptor.WriteEndDocument();
                engineDescriptor.Close();
            }

            System.Diagnostics.Debug.Write("Writing out XML engine descriptor finished.");
        }

        private void WriteScene(XmlWriter engineDescriptor, SceneNode RootSceneNode)
        {
            //loop through all SceneNodes and save them in xml

            //<Scene>
            //    <node name="Robot">
            //        <position x="10.0" y="5" z="10.5" />
            //        <scale x="1" y="1" z="1" />
            //        <Orientation x="0" y="0" z="0" w="1" />

            //        <objects>
            //            <entity name="Robot" mesh="robot.mesh" />
            //            <light name="Omni01" type="point" intensity="0.01" contrast="0">
            //                <colourDiffuse r="0.4" g="0.4" b="0.5" />
            //                <colourSpecular r="0.5" g="0.5" b="0.5" />
            //            </light>
            //        </objects>

            //        <node name="Robot"></node>
            //        <node name="Robot"></node>
            //    </node>	
            //</Scene>

            engineDescriptor.WriteStartElement("Scene");

            WriteSceneNode(engineDescriptor, RootSceneNode);

            engineDescriptor.WriteEndElement();
        }

        private void WriteSceneNode(XmlWriter engineDescriptor, SceneNode SingleSceneNode)
        {
            engineDescriptor.WriteStartElement("Node");
            engineDescriptor.WriteAttributeString("Name", SingleSceneNode.Name);

            engineDescriptor.WriteStartElement("Position");
            engineDescriptor.WriteAttributeString("X", SingleSceneNode.Position.x.ToString());
            engineDescriptor.WriteAttributeString("Y", SingleSceneNode.Position.y.ToString());
            engineDescriptor.WriteAttributeString("Z", SingleSceneNode.Position.z.ToString());
            engineDescriptor.WriteEndElement();

            engineDescriptor.WriteStartElement("Scale");
            engineDescriptor.WriteAttributeString("X", SingleSceneNode.Scale.x.ToString());
            engineDescriptor.WriteAttributeString("Y", SingleSceneNode.Scale.y.ToString());
            engineDescriptor.WriteAttributeString("Z", SingleSceneNode.Scale.z.ToString());
            engineDescriptor.WriteEndElement();

            engineDescriptor.WriteStartElement("Orientation");
            engineDescriptor.WriteAttributeString("X", SingleSceneNode.Orientation.x.ToString());
            engineDescriptor.WriteAttributeString("Y", SingleSceneNode.Orientation.y.ToString());
            engineDescriptor.WriteAttributeString("Z", SingleSceneNode.Orientation.z.ToString());
            engineDescriptor.WriteAttributeString("W", SingleSceneNode.Orientation.w.ToString());
            engineDescriptor.WriteEndElement();

            //write node's objects
            engineDescriptor.WriteStartElement("Objects");
            foreach (MovableObject obj in SingleSceneNode.Objects)
            {
                engineDescriptor.WriteStartElement(obj.MovableType);
                engineDescriptor.WriteAttributeString("Name", obj.Name);
                engineDescriptor.WriteAttributeString("CastShadows", obj.CastShadows.ToString());
                
                //write special attributes or inner elements of a type
                //TODO: add other movableObject types to this list
                if (obj.MovableType == "Entity")
                    WriteEntityParams(engineDescriptor, (Entity)obj);

                engineDescriptor.WriteEndElement();
            }
            engineDescriptor.WriteEndElement();

            //recall itself to write inner nodes
            foreach (SceneNode subNode in SingleSceneNode.Children)
            {
                if (ignoredSceneNodeNames.Contains(subNode.Name))
                    continue;
                WriteSceneNode(engineDescriptor, subNode);
            }

            engineDescriptor.WriteEndElement();
        }

        private void WriteEntityParams(XmlWriter engineDescriptor, Entity entity)
        {
            engineDescriptor.WriteStartElement("NamedParameter");
            engineDescriptor.WriteAttributeString("Key", "mesh");
            engineDescriptor.WriteAttributeString("Value", entity.Mesh.Name);
            engineDescriptor.WriteEndElement();
        }

        private void WriteResourcesConfig(XmlWriter engineDescriptor, string FileRootDirectory)
        {
            engineDescriptor.WriteStartElement("Resources");
            //DefaultMipmapCount count to be saved in resource's attributes
            engineDescriptor.WriteAttributeString("DefaultMipmapCount",
                DefaultMipmapCount.ToString());

            foreach (Resource res in ResourcesList)
            {
                engineDescriptor.WriteStartElement("Resource");
                //resource locations most change to local before save
                engineDescriptor.WriteAttributeString("Location",
                    res.Location.Replace(FileRootDirectory + Path.DirectorySeparatorChar, ""));
                engineDescriptor.WriteAttributeString("Group", res.Group);
                engineDescriptor.WriteAttributeString("Type", res.Type);
                engineDescriptor.WriteAttributeString("Recursive", res.Recursive.ToString());

                engineDescriptor.WriteEndElement();
            }
            engineDescriptor.WriteEndElement();
        }

        private void WriteTerrainConfig(XmlWriter engineDescriptor)
        {
            if (!String.IsNullOrEmpty(Terrain))
                engineDescriptor.WriteElementString("Terrain", Terrain);
        }

        private void WriteCoreConfigs(XmlWriter engineDescriptor, ConfigOptionCollection configOptions)
        {
            engineDescriptor.WriteStartElement("Core");

            //write rendersystem configs
            engineDescriptor.WriteStartElement("RenderSystem");
            engineDescriptor.WriteAttributeString("Key", RenderSystemKey);

            foreach (ConfigOption cfg in configOptions)
            {
                engineDescriptor.WriteStartElement("ConfigOption");

                engineDescriptor.WriteElementString("Name", cfg.Name);
                engineDescriptor.WriteElementString("Value", cfg.Value);

                engineDescriptor.WriteEndElement();
            }

            engineDescriptor.WriteEndElement();

            //write SceneManagerType
            engineDescriptor.WriteStartElement("SceneManagerType");
            engineDescriptor.WriteString(SceneType.ToString());

            engineDescriptor.WriteEndElement();

            //write SceneManagerName if assigned
            if (!String.IsNullOrEmpty(SceneTypeName))
            {
                engineDescriptor.WriteStartElement("SceneManagerName");
                engineDescriptor.WriteString(SceneTypeName);

                engineDescriptor.WriteEndElement();
            }

            engineDescriptor.WriteEndElement();
        }
    }
}